/*
 * Copyright 2011, Vladimir Kostyukov & Alexei Zavjalov
 * 
 * This file is part of losharik project (http://losharik.googlecode.com)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *      
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var DEFAULT_FPS = 30;

var DEFAULT_PLATFORM_HEIGHT = 13;
var DEFAULT_PLATFORM_WIDTH = 80;

var DEFAULT_ROLL_HEIGHT = 24;
var DEFAULT_ROLL_WIDTH = 24;

var DEFAULT_MIN_PLATFORM_INTERVAL = 50;
var DEFAULT_MAX_PLATFORM_INTERVAL = 100;

var DEFAULT_WORLD_SPEED = 3;
var DEFAULT_ROLL_SPEED = 4;
var DEFAULT_BONUS_FALL_SPEED = 6;

var DEFAULT_WORLD_SPEED_ACCEL = 1;

var MINIMUM_SOLID_PLATFORMS = 2;

var DEFAULT_LIFES = 3;

var DEFAUL_AFTERDIE_VALUE = DEFAULT_FPS * 2;

var HEIGHT = 480;
var WIDTH = 320;

var DEFAULT_SCORE_NORMALIZER = 4000;

var DEFAULT_BONUS_HEIGHT = 16;
var DEFAULT_BONUS_WIDTH = 16;

var ROLL_MOVING_TYPE = {
	LEFT : {value: 1},
	RIGHT: {value: 2},
	NO   : {value: 0}
};

var DIFFICULTY_LEVELS = [ 0, 2000, 4000, 8000, 16000, 25000, Number.MAX_VALUE ];
var MAX_DIFFICULTY_LEVEL = DIFFICULTY_LEVELS.length - 1;

var PLATFORM_TYPE = {
	SOLID : {value: 1},
	KILLER: {value: 2},
	ICE   : {value: 3}
};

var SLIDE_PLATFORM_DIR = {
	LEFT: {value: 1},
	RIGHT: {value: 2}
};

var SLIDE_PLATFORM_SPEED = 2;

var BONUS_TYPE = {
	LIVE: {value: 1}
};

var DEFAULT_ICE_PLATFORM_LIFETIME = 35;

var ROLL_X_ACCELERATION_VALUE = 6.0;
var DEFAULT_ROLL_ACCEL_DEC = 0.4;

var DEFAULT_GRAVITY = 0.2;

/**
 * Game model class; 
 * 
 */
function GameModel() {

	var pCounter = 0;
	var rollAccelX = 0.0;
	var rollAccelY = 0.0;
	
	var rollDie = false;
	var gameDifficultyLevel = 0;
	var level = 0;
	
	var gravity = DEFAULT_GRAVITY;

	var rollMoveRight = false;
	var rollMoveLeft = false;
	
	var rollSpeed = DEFAULT_ROLL_SPEED;
	
	var score = 0;
	this.score = function() { return score; };
	
	var lifes = 0;
	this.lifes = function() { return lifes; }; 
	
	var platforms = { };
	this.platforms = function() { return platforms; };
	
	var bonuses = { };
	this.bonuses = function() { return bonuses; };
	
	var roll = { };
	this.roll = function() { return roll; };

	var view = null;
	this.setView = function(v) { view = v; };
	this.getView = function() { return view; };
	
	var paused = false;
	this.paused = function() { return paused; };
	
	var afterdie = 0;
	this.afterdiePaused = function() { return afterdie > 0; };
	
	var worldSpeed = DEFAULT_WORLD_SPEED;
	this.worldSpeed = function() { return worldSpeed; };

	this.startMoveLeft = function() { 
		rollMoveLeft = true;
	};
	
	this.startMoveRight = function() {
		rollMoveRight = true;
	};
	
	this.stopMoveRight = function() {
		rollMoveRight = false;
	};

	this.stopMoveLeft = function() {
		rollMoveLeft = false;
	};
	
	this.tooglePause = function() {
		paused = !paused;
	};

	var onLose = function() { return; }
	this.onLose = function(handler) { onLose = handler; };
	this.losed = function() { return lifes <= 0 && rollDie;}
	
	this.init = function() {
		
		generatePlatforms();
		generateRoll();
		
		lifes = DEFAULT_LIFES;
		
	};

	// build next/new frame
	this.next = function() {
		
		if (paused) {
			view.update();
			return;
		}
		
		if (afterdie > 0) {
			afterdie--;
			view.update();
			return;
		}

		if (rollDie) {
			lifes--;
			if (lifes >= 0) {
				rollDie = false;
				generateRoll();
			} else {
				onLose();
				view.update();
				return;
			}
		}

		if (DIFFICULTY_LEVELS[level] <= score) { level++; };
		
		if (level < MAX_DIFFICULTY_LEVEL)
			gameDifficultyLevel =  level + 1 / ((DIFFICULTY_LEVELS[level] - DIFFICULTY_LEVELS[level - 1]) /
			((score + 1) - DIFFICULTY_LEVELS[level - 1])) - 1;

		rollSpeed = DEFAULT_ROLL_SPEED + gameDifficultyLevel / 5;
		
		if (rollMoveLeft) rollAccelX = -ROLL_X_ACCELERATION_VALUE - gameDifficultyLevel;
		if (rollMoveRight) rollAccelX = ROLL_X_ACCELERATION_VALUE + gameDifficultyLevel;
		
		var rollAccelDec = DEFAULT_ROLL_ACCEL_DEC + gameDifficultyLevel / 5;

		rollAccelY += gravity; 			

		nextRoll = {x: roll.x + rollAccelX, y: roll.y + rollSpeed + rollAccelY, w: roll.w, h: roll.h};
		
		if (nextRoll.x > WIDTH) nextRoll.x = -nextRoll.w;
		if (nextRoll.x + nextRoll.w < 0) nextRoll.x = WIDTH - nextRoll.w;

		nextRoll.moving = roll.moving;
		
		if (rollAccelX >= rollAccelDec / 2) {
			nextRoll.moving = ROLL_MOVING_TYPE.RIGHT;
			rollAccelX -= rollAccelDec;
		} else if (rollAccelX <= -rollAccelDec / 2) {
			nextRoll.moving = ROLL_MOVING_TYPE.LEFT;
			rollAccelX += rollAccelDec;
		} else {
			rollAccelX = 0;
		}
		
		processBonuses();
		
		processPlatforms();
		
		roll = nextRoll;
		
		score += Math.floor(rollSpeed + worldSpeed);
		
		worldSpeed = DEFAULT_WORLD_SPEED + gameDifficultyLevel / 2;
		gravity = DEFAULT_GRAVITY + (gameDifficultyLevel / 10);
		
		view.update(); //update view
	};

	var processPlatforms = function() {
		
		for (var index in platforms) {

			var platform = platforms[index];
			platform.y -= worldSpeed;

			if (platform.y + platform.h  < 0) {
				delete platforms[index];
				delete bonuses[index];
				generatePlatforms();
				continue;
			}
			
			if (isRollCrossPlatform(platform, roll, nextRoll)) {
				
				nextRoll.y = platform.y - nextRoll.h;
				rollSpeed = -worldSpeed;
				rollAccelY = 0.0;
				
				if (platform.type == PLATFORM_TYPE.ICE) {
					if (!platform.activated) platform.activated = true;
				}
				
				if (platform.slide) {
					nextRoll.x += (platform.dir == SLIDE_PLATFORM_DIR.LEFT) ? -SLIDE_PLATFORM_SPEED : SLIDE_PLATFORM_SPEED;
				}
				
			}

			if (platform.type == PLATFORM_TYPE.ICE && platform.activated) {
				platform.lifeTime -= 1 + gameDifficultyLevel / 10;
				if (platform.lifeTime <= 0) {
					delete platforms[index];
					generatePlatforms();
					continue;
				}
			}
			
			if (platform.type == PLATFORM_TYPE.KILLER) {
				if (objCollision(platform, nextRoll)) {
					
					die();
					return;
				}
			}
			
			if (platform.slide) {
				if (platform.dir == SLIDE_PLATFORM_DIR.LEFT) {
					platform.x -= SLIDE_PLATFORM_SPEED;
					if (platform.x <= 0) platform.dir = SLIDE_PLATFORM_DIR.RIGHT;
				} else {
					platform.x += SLIDE_PLATFORM_SPEED;
					if (platform.x + platform.w >= WIDTH) platform.dir = SLIDE_PLATFORM_DIR.LEFT;
				}
			}
		}
		
		if ((nextRoll.y > HEIGHT && rollSpeed > 0) || nextRoll.y < 0) {

			die();
			return;
		}
	}
	
	var processBonuses = function() {

		for (var index in bonuses) {
			var bonus = bonuses[index];
			
			if (objCollision(nextRoll, bonus)) {
				
				if (bonus.type == BONUS_TYPE.LIVE) {
					lifes++;
				}
				
				delete bonuses[index];

				continue;
			}

			if (platforms[index] == undefined) {

				bonus.y += DEFAULT_BONUS_FALL_SPEED;

				if (bonus.y > HEIGHT) {
					delete bonuses[index];
				}

			} else {
				if (platforms[index].slide) {
					bonus.x += (platforms[index].dir == SLIDE_PLATFORM_DIR.LEFT) ?
						-SLIDE_PLATFORM_SPEED :
						SLIDE_PLATFORM_SPEED;
				}
			
				bonus.y -= worldSpeed;
			}
		}
	}
	
	var isRollCrossPlatform = function(platform, rollPrev, rollNext) {

		// we should to check only platforms between prev roll and next roll
		if (platform.y <= rollPrev.y || platform.y > rollNext.y + rollNext.h) return false;
	
		var s1 = { x1: platform.x, y1: platform.y, x2: platform.x + platform.w, y2: platform.y };
		
		var s2 = { 
			x1: rollPrev.x, y1: rollPrev.y + rollPrev.h - worldSpeed, 
			x2: rollNext.x, y2: rollNext.y + rollNext.h 
		};
		
		var s3 = { 
			x1: rollPrev.x + rollPrev.w, y1: rollPrev.y + rollPrev.h - worldSpeed, 
			x2: rollNext.x + rollNext.w, y2: rollNext.y + rollNext.h 
		};
		
		var zn1 = (s2.y2 - s2.y1) * (s1.x2 - s1.x1) - (s2.x2 - s2.x1) * (s1.y2 - s1.y1);
		var zn2 = (s3.y2 - s3.y1) * (s1.x2 - s1.x1) - (s3.x2 - s3.x1) * (s1.y2 - s1.y1);

		if (Math.abs(zn1) < Math.EPS && Math.abs(zn2) < Math.EPS) return false;
			
		var ch11 = (s2.x2 - s2.x1) * (s1.y1 - s2.y1) - (s2.y2 - s2.y1) * (s1.x1 - s2.x1);
		var ch21 = (s1.x2 - s1.x1) * (s1.y1 - s2.y1) - (s1.y2 - s1.y1) * (s1.x1 - s2.x1);
		
		if ((ch11/zn1 <= 1.0 && ch11/zn1 >= 0.0) && (ch21/zn1 <= 1.0 && ch21/zn1 >= 0.0)) {
			return true;
		} 
		
		var ch12 = (s3.x2 - s3.x1) * (s1.y1 - s3.y1) - (s3.y2 - s3.y1) * (s1.x1 - s3.x1);
		var ch22 = (s1.x2 - s1.x1) * (s1.y1 - s3.y1) - (s1.y2 - s1.y1) * (s1.x1 - s3.x1);

		if ((ch12/zn2 <= 1.0 && ch12/zn2 >= 0.0) && (ch22/zn2 <= 1.0 && ch22/zn2 >= 0.0)) {
			return true;
		}
		
		return false;
	};
	
	var objCollision = function(obj1, obj2) {
		
		var xColl = false; 
		var yColl = false; 
		if ((obj1.x + obj1.w >= obj2.x) && (obj1.x <= obj2.x + obj2.w)) xColl = true;
		if ((obj1.y + obj1.h >= obj2.y) && (obj1.y <= obj2.y + obj2.h)) yColl = true;
		if (xColl & yColl) { return true; };
		return false;
	};
	
	var die = function() {

		rollAccelX = 0.0;
		afterdie = DEFAUL_AFTERDIE_VALUE;
		rollDie = true;
	};
	
	var generateBonusOnIndexedPlatform = function(index) {

		var platform = platforms[index];
		var bonusType = BONUS_TYPE.LIVE;
		bonuses[index] = {
			x: platform.x + Math.floor(Math.random() * (DEFAULT_PLATFORM_WIDTH - (DEFAULT_BONUS_WIDTH))),
			y: platform.y - DEFAULT_BONUS_HEIGHT,
			w: DEFAULT_BONUS_WIDTH,
			h: DEFAULT_BONUS_HEIGHT,
			type: bonusType
		};
	};
	
	var generatePlatforms = function() {
		
		// platforms interval increases with difficult multiplied to 5
		var minPlatformInterval = DEFAULT_MIN_PLATFORM_INTERVAL + gameDifficultyLevel * 5;
		var maxPlatformInterval = DEFAULT_MAX_PLATFORM_INTERVAL + gameDifficultyLevel * 5;
		
		// Platforms appearance probability in percent
		var MAX_PERCENT = 100;
		var solidPlatformProb = 5;
		var killerPlatformProb = solidPlatformProb + gameDifficultyLevel * (40 / MAX_DIFFICULTY_LEVEL);
		var icePlatformProb = killerPlatformProb +  gameDifficultyLevel * (40 / MAX_DIFFICULTY_LEVEL);

		var slidePlatformProb = 20;
		
		var bonusProb = 1; // %
		
		do {
			var platformType;
	
			var randomValue = Math.floor(Math.random() * MAX_PERCENT);

			if (randomValue <= solidPlatformProb) platformType = PLATFORM_TYPE.SOLID;
			else if (randomValue <= killerPlatformProb && gameDifficultyLevel >= 1 ) platformType = PLATFORM_TYPE.KILLER;
			else if (randomValue <= icePlatformProb && gameDifficultyLevel >= 2) platformType = PLATFORM_TYPE.ICE;
			else platformType = PLATFORM_TYPE.SOLID;
				
			var baseline = pCounter > 0 ? platforms[pCounter - 1].y : 0;
			platforms[pCounter] = {
				x: Math.floor(Math.random() * (WIDTH - DEFAULT_PLATFORM_WIDTH)),
				y: Math.floor(baseline + (Math.random() * (maxPlatformInterval - minPlatformInterval + 1)) + minPlatformInterval),
				w: DEFAULT_PLATFORM_WIDTH,
				h: DEFAULT_PLATFORM_HEIGHT,
				type: platformType,
				slide: (Math.random() * MAX_PERCENT < slidePlatformProb) ? true : false
			};
			
			if (platformType == PLATFORM_TYPE.ICE) {
				platforms[pCounter].lifeTime = DEFAULT_ICE_PLATFORM_LIFETIME;
				platforms[pCounter].activated = false;
			}
			
			if (platforms[pCounter].slide) {
				var SLIDE_PLATFORM_DIR_PROB = 50;
				platforms[pCounter].dir = (Math.random() * MAX_PERCENT > SLIDE_PLATFORM_DIR_PROB) ?
					SLIDE_PLATFORM_DIR.LEFT :
					SLIDE_PLATFORM_DIR.RIGHT;
			}			
			
			// generate bonuses
			if (Math.random() * MAX_PERCENT < bonusProb) { // FIXME: 
				generateBonusOnIndexedPlatform(pCounter);
			}

			pCounter++;
		} while (platforms[pCounter - 1].y < (HEIGHT + DEFAULT_PLATFORM_HEIGHT));
		
		// at least one platform should have a solid type
		var totalSolidPlatforms = 0;
		for (var index in platforms) {
			var platform = platforms[index];	
			if (platform.type == PLATFORM_TYPE.SOLID) totalSolidPlatforms++;
		}
		if (totalSolidPlatforms <= MINIMUM_SOLID_PLATFORMS &&
			platforms[pCounter - 2].type != PLATFORM_TYPE.SOLID)
			platforms[pCounter - 1].type = PLATFORM_TYPE.SOLID;
	};
	
	var generateRoll = function() {
		
		for (var index in platforms) {
			var platform = platforms[index];
			if (platform.y > HEIGHT) {
				platform.type = PLATFORM_TYPE.SOLID;
				
				var hh = DEFAULT_ROLL_HEIGHT, ww = DEFAULT_ROLL_WIDTH;
				var yy = platform.y - hh - worldSpeed;
				var xx = platform.x + (platform.w / 2) - ww / 2;
				
				roll = {h: hh, w: ww, x: xx, y: yy, moving: ROLL_MOVING_TYPE.RIGHT};
				return;
			}
		}
	};
}

/**
 * HTML5 implementation;
 * 
 */
function HTML5View(resources) {

	var FPS_REFRESH_INTERVAL = 10;

	var model = null;
	this.setModel = function(m) { model = m; };
	this.getModel = function() { return model; };
	
	var context = resources.CANVAS.getContext("2d");
	context.font = "bold 18px/2.8 arial";
	
	var bgPosition = 0;

	var frameCounter = 0;
	var lastTime = new Date().getTime();
	var totalTime = 0;

	this.update = function() {

		if (model.paused()) {
			message("PAUSED");
			return;
		}

		if (model.losed()) {
			message("YOU LOSE");
			return;
		}

		if (model.afterdiePaused()) {
			return;
		}

		// draw background
		bgPosition = (bgPosition - model.worldSpeed() <= -HEIGHT) ? 0 : bgPosition - model.worldSpeed();
		context.drawImage(resources.BACKGROUND, 0, bgPosition, WIDTH, HEIGHT);
		context.drawImage(resources.BACKGROUND, 0, bgPosition + HEIGHT, WIDTH, HEIGHT);

		// draw platforms
		var platforms = model.platforms();
		for (var index in platforms) {
			
			var platform = platforms[index];
			
			if (platform.type == PLATFORM_TYPE.SOLID || platform.type == PLATFORM_TYPE.SLIDE) {
				
				context.drawImage(resources.PLATFORM, platform.x, platform.y, platform.w, platform.h);

			} else if (platform.type == PLATFORM_TYPE.KILLER) {
				
				context.drawImage(resources.KILLER_PLATFORM, platform.x, platform.y, platform.w, platform.h);
				
			} else if (platform.type == PLATFORM_TYPE.ICE) {
				
				var a = 1 / DEFAULT_ICE_PLATFORM_LIFETIME * platform.lifeTime;
				context.globalAlpha = a;
				context.drawImage(resources.ICE_PLATFORM, platform.x, platform.y, platform.w, platform.h);
			    context.globalAlpha = 1.0;
			}
		}
		
		var bonuses = model.bonuses();
		for (var index in bonuses) {
			
			var bonus = bonuses[index];
			
			if (bonus.type == BONUS_TYPE.LIVE) {

				context.drawImage(resources.LIVE, bonus.x, bonus.y, bonus.w, bonus.h);

			}
		}
		
		// draw roll
		var roll = model.roll();
		
		if (roll.moving == ROLL_MOVING_TYPE.RIGHT || roll.moving == ROLL_MOVING_TYPE.NO) {
			context.drawImage(resources.ROLL_RIGHT, roll.x, roll.y, roll.w, roll.h);	
		} else {
			context.drawImage(resources.ROLL_LEFT, roll.x, roll.y, roll.w, roll.h);
		}

		// draw score
		var score = model.score();
		context.drawImage(resources.SCORE, 4, 6, resources.SCORE.width, resources.SCORE.height);
		context.fillStyle="#777777";
		context.fillText(score, 22, 22);

		// draw lifes
		var lifes = model.lifes();
		context.drawImage(resources.LIVE, WIDTH - 40, 8, 14, 14);
		context.fillStyle="#777777";
		context.fillText(lifes, WIDTH - 22, 22);

		// draw FPS
		var fps = (frameCounter / totalTime) * 1000.0;
		context.fillText(fps.toFixed(2) + " fps", 4, HEIGHT - 6);

		// calc FPS
		if (frameCounter > FPS_REFRESH_INTERVAL) { frameCounter = 0; totalTime  = 0; }

		frameCounter++;
		var currentTime = new Date().getTime();
		totalTime += (currentTime - lastTime);
		lastTime = currentTime;
	};

	this.clear = function() {
		context.clearRect(0, 0, WIDTH, HEIGHT);
	}

	this.menu = function() {

		resources.MENU.style.display = "block";
		resources.RECORDS.style.display="none";
		resources.HELP.style.display="none";
		resources.CANVAS.style.display="none";

	};

	this.play = function() {

		resources.MENU.style.display = "none";
		resources.RECORDS.style.display="none";
		resources.HELP.style.display="none";
		resources.CANVAS.style.display="block";

	};

	this.records = function(data) {

		resources.MENU.style.display = "none";
		resources.RECORDS.style.display="block";
		resources.HELP.style.display="none";
		resources.CANVAS.style.display="none";

		var content = resources.RECORDS.childNodes[1];
		content.innerHTML = data;

	};

	this.help = function() {

		resources.MENU.style.display = "none";
		resources.RECORDS.style.display="none";
		resources.HELP.style.display="block";
		resources.CANVAS.style.display="none";

	};

	var message = function(text) {

		context.save();
		context.textAlign = "center";
		context.font = "bold 26px/3.8 arial";
		context.fillStyle= "#777777";
		context.fillText(text, WIDTH / 2, HEIGHT / 2);
		context.restore();
	};
}

/**
 * Default keyboard controller;
 * 
 */
function KeyboardController() {
	
	var LEFT_KEY_CODE = 37;
	var RIGHT_KEY_CODE = 39;
	var PAUSE_KEY_CODE = 19;
	var P_KEY_CODE = 80;
	
	var model = null;
	this.setModel = function(m) { model = m; };
	this.getModel = function() { return model; };
	
	this.keydown = function(event) {
		
		if (event.keyCode == LEFT_KEY_CODE) {
			
			model.startMoveLeft();

		} else if (event.keyCode == RIGHT_KEY_CODE) {
			
			model.startMoveRight();
			
		} else if (event.keyCode == PAUSE_KEY_CODE || event.keyCode == P_KEY_CODE) {
			
			model.tooglePause();
			
		}
	};

	this.keyup = function(event) {
		
		if (event.keyCode == LEFT_KEY_CODE) {

			model.stopMoveLeft();

		} else if (event.keyCode == RIGHT_KEY_CODE) {
			
			model.stopMoveRight();
			
		}
	};
	
	document.onkeydown = this.keydown;
	document.onkeyup = this.keyup;
}

/**
 * Game class;
 * 
 */
function Game(model, view, ctrl) {

	var self = this;

	view.setModel(model);
	ctrl.setModel(model);
	model.setView(view);

	var interrupter = null;

	model.onLose(
		function() {
			clearInterval(interrupter);

			var request = createXMLHttpRequest();

			if (request != null) {
				request.open("GET", "http://losharik.u-gu.ru/records_table.php?last", true);
				request.onreadystatechange = function() {
				if (request.readyState == 4 && request.status == 200) {
					var last = parseInt(request.responseText);
					if (model.score() > last) {
						self.submit();
						self.restart();
						self.records();
						view.clear();
					} else {
						self.restart();
						self.menu();
						view.clear();
					}
				}
				};
			request.send(null);
			} else {
   				window.alert("AJAX (XMLHTTP) not supported.");
			}
		}
	);

	this.play = function() {

		view.play();
		
		model.init();
		interrupter = setInterval(model.next, 1000 / DEFAULT_FPS);
		
	};

	this.menu = function() { view.menu(); };

	this.records = function() {

		var request = createXMLHttpRequest();

		if (request != null) {
			request.open("GET", "http://losharik.u-gu.ru/records_table.php?get", true);
			request.onreadystatechange = function() {
				if (request.readyState == 4 && request.status == 200) {
					view.records(request.responseText);
				}
			};
			request.send(null);
		} else {
   			window.alert("AJAX (XMLHTTP) not supported.");
		}
	};
	
	this.help = function() { view.help(); };

	this.submit = function() {

		var username = prompt("Enter your name for submiting","Losharik");
		var score = model.score();

		var request = createXMLHttpRequest();

		if (request != null) {
			request.open("POST", "http://losharik.u-gu.ru/records_table.php?add&name=" + username + "&score=" + score, true);
			request.send(null);
		} else {
   			window.alert("AJAX (XMLHTTP) not supported.");
		}
	};

	var createXMLHttpRequest = function() {
		if (window.XMLHttpRequest) return new window.XMLHttpRequest;
		else {
			try {
				return new ActiveXObject("MSXML2.XMLHTTP");
			} catch(ex) {
				return null;
			}
		}
	}
}
